﻿using Autodesk.Revit.DB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ZhuXingFamilys.AuditIndicators.utils
{
    public class SectionUtils
    {
        /// <summary>
        /// 从左往右看的剖面(类似于西立面)
        /// </summary>
        /// <param name="modelCoordinateMin">要生成的剖面视图框在世界坐标系中的Min点</param>
        /// <param name="modelCoordinateMax">要生成的剖面视图框在世界坐标系中的Max点</param>
        /// <returns></returns>
        public BoundingBoxXYZ SetBoundingBoxXYZFromLeftToRight(XYZ modelCoordinateMin, XYZ modelCoordinateMax)
        {
            var transform = GetTransformFromLeftToRight();
            //transform.OfPoint可以将局部坐标系转到世界坐标系
            //transform.Inverse.OfPoint可以将世界坐标系转到局部坐标系
            var tempMin = transform.Inverse.OfPoint(modelCoordinateMin);
            var tempMax = transform.Inverse.OfPoint(modelCoordinateMax);
            //先将世界的最大最小转到局部，然后需要对其进行重组，因为原来的最大/小现在不一定是最大/小
            //转完的点不一定还是最大最小点，但是这几个X,Y,Z值仍然是BoundingBox的角点
            //所以将他们重组一下，变为新的最大最小点
            //谁大谁就是最大值，谁小谁就是最小值
            //var minActural = new XYZ
            //    (tempMin.X < tempMax.X ? tempMin.X : tempMax.X
            //    , tempMin.Y < tempMax.Y ? tempMin.Y : tempMax.Y
            //    , tempMin.Z < tempMax.Z ? tempMin.Z : tempMax.Z);
            //var maxActural = new XYZ
            //    (tempMin.X < tempMax.X ? tempMax.X : tempMin.X
            //    , tempMin.Y < tempMax.Y ? tempMax.Y : tempMin.Y
            //    , tempMin.Z < tempMax.Z ? tempMax.Z : tempMin.Z);
            var minActural = GetActualMinPoint(tempMin, tempMax);
            var maxActural = GetActualMaxPoint(tempMin, tempMax);

            var sectionBoundingBoxXyz = new BoundingBoxXYZ
            {
                Min = minActural,
                Max = maxActural,
                Transform = transform
            };
            return sectionBoundingBoxXyz;
        }

        /// <summary>
        /// 计算出从左向右看的转换Transform
        /// </summary>
        /// <returns></returns>
        private Transform GetTransformFromLeftToRight()
        {
            var transform = Transform.Identity;
            transform.Origin = XYZ.Zero;
            //指定了生成的剖面的UpDirection
            transform.BasisY = XYZ.BasisZ;
            //指定了生成的剖面的ViewDirection的反方向！！！即：如果是从左往右看的剖面，按道理，生成的剖面的viewDirection是-x,这里直接取x
            transform.BasisZ = XYZ.BasisX;
            //由UpDirection和ViewDirection，根据右手系，定出RightDirection
            transform.BasisX = XYZ.BasisY;
            return transform;
        }

        /// <summary>
        /// 从右往左看的剖面(类似于东立面)
        /// </summary>
        /// <param name="modelCoordinateMin">要生成的剖面视图框在世界坐标系中的Min点</param>
        /// <param name="modelCoordinateMax">要生成的剖面视图框在世界坐标系中的Max点</param>
        /// <returns></returns>
        public BoundingBoxXYZ SetBoundingBoxXYZFromRightToLeft(XYZ modelCoordinateMin, XYZ modelCoordinateMax)
        {
            var transform = GetTransformFromRightToLeft();

            var tempMin = transform.Inverse.OfPoint(modelCoordinateMin);
            var tempMax = transform.Inverse.OfPoint(modelCoordinateMax);

            var minActural = GetActualMinPoint(tempMin, tempMax);
            var maxActural = GetActualMaxPoint(tempMin, tempMax);

            var sectionBoundingBoxXyz = new BoundingBoxXYZ
            {
                Min = minActural,
                Max = maxActural,
                Transform = transform
            };
            return sectionBoundingBoxXyz;
        }

        /// <summary>
        /// 计算出从右向左看的转换Transform
        /// </summary>
        /// <returns></returns>
        private Transform GetTransformFromRightToLeft()
        {
            var transform = Transform.Identity;
            transform.Origin = XYZ.Zero;
            //指定了生成的剖面的UpDirection
            transform.BasisY = XYZ.BasisZ;
            //指定了生成的剖面的ViewDirection的反方向！！！即：如果是从左往右看的剖面，按道理，生成的剖面的viewDirection是-x,这里直接取x
            transform.BasisZ = -XYZ.BasisX;
            //由UpDirection和ViewDirection，根据右手系，定出RightDirection
            transform.BasisX = -XYZ.BasisY;
            return transform;
        }

        /// <summary>
        /// 从下往上看的剖面(类似于南立面)
        /// </summary>
        /// <param name="modelCoordinateMin">要生成的剖面视图框在世界坐标系中的Min点</param>
        /// <param name="modelCoordinateMax">要生成的剖面视图框在世界坐标系中的Max点</param>
        /// <returns></returns>
        public BoundingBoxXYZ SetBoundingBoxXYZFromDownToUp(XYZ modelCoordinateMin, XYZ modelCoordinateMax)
        {
            var transform = GetTransformFromDownToUp();

            var tempMin = transform.Inverse.OfPoint(modelCoordinateMin);
            var tempMax = transform.Inverse.OfPoint(modelCoordinateMax);

            var minActural = GetActualMinPoint(tempMin, tempMax);
            var maxActural = GetActualMaxPoint(tempMin, tempMax);

            var sectionBoundingBoxXyz = new BoundingBoxXYZ
            {
                Min = minActural,
                Max = maxActural,
                Transform = transform
            };

            return sectionBoundingBoxXyz;
        }

        /// <summary>
        /// 计算出从下向上看的转换Transform
        /// </summary>
        /// <returns></returns>
        private Transform GetTransformFromDownToUp()
        {
            var transform = Transform.Identity;
            transform.Origin = XYZ.Zero;
            //指定了生成的剖面的UpDirection
            transform.BasisY = XYZ.BasisZ;
            //指定了生成的剖面的ViewDirection的反方向！！！即：如果是从下往上看的剖面，按道理，生成的剖面的viewDirection是-y,这里直接取y
            transform.BasisZ = XYZ.BasisY;
            //由UpDirection和ViewDirection，根据右手系，定出RightDirection
            transform.BasisX = -XYZ.BasisX;
            return transform;
        }

        /// <summary>
        /// 从上往下看的剖面(类似于北立面)
        /// </summary>
        /// <param name="modelCoordinateMin">要生成的剖面视图框在世界坐标系中的Min点</param>
        /// <param name="modelCoordinateMax">要生成的剖面视图框在世界坐标系中的Max点</param>
        /// <returns></returns>
        public BoundingBoxXYZ SetBoundingBoxXYZFromUpToDown(XYZ modelCoordinateMin, XYZ modelCoordinateMax)
        {
            var transform = GetTransformFromUpToDown();

            var tempMin = transform.Inverse.OfPoint(modelCoordinateMin);
            var tempMax = transform.Inverse.OfPoint(modelCoordinateMax);

            var minActural = GetActualMinPoint(tempMin, tempMax);
            var maxActural = GetActualMaxPoint(tempMin, tempMax);

            var sectionBoundingBoxXyz = new BoundingBoxXYZ
            {
                Min = minActural,
                Max = maxActural,
                Transform = transform
            };
            return sectionBoundingBoxXyz;
        }

        /// <summary>
        /// 计算出从上向下看的转换Transform
        /// </summary>
        /// <returns></returns>
        private Transform GetTransformFromUpToDown()
        {
            var transform = Transform.Identity;
            transform.Origin = XYZ.Zero;
            //指定了生成的剖面的UpDirection
            transform.BasisY = XYZ.BasisZ;
            //指定了生成的剖面的ViewDirection的反方向！！！即：如果是从下往上看的剖面，按道理，生成的剖面的viewDirection是-y,这里直接取y
            transform.BasisZ = -XYZ.BasisY;
            //由UpDirection和ViewDirection，根据右手系，定出RightDirection
            transform.BasisX = XYZ.BasisX;
            return transform;
        }

        /// <summary>
        /// 根据输入的两个点（一个矩形框的2个角点），计算出这个矩形框真实的最小点（左下后）
        /// </summary>
        /// <param name="tempMin"></param>
        /// <param name="tempMax"></param>
        /// <returns></returns>
        private XYZ GetActualMinPoint(XYZ tempMin, XYZ tempMax)
        {
            var minActural = new XYZ
                (tempMin.X < tempMax.X ? tempMin.X : tempMax.X
                , tempMin.Y < tempMax.Y ? tempMin.Y : tempMax.Y
                , tempMin.Z < tempMax.Z ? tempMin.Z : tempMax.Z);
            return minActural;
        }


        /// <summary>
        /// 根据输入的两个点（一个矩形框的2个角点），计算出这个矩形框真实的最大点（右上前）
        /// </summary>
        /// <param name="tempMin"></param>
        /// <param name="tempMax"></param>
        /// <returns></returns>
        private XYZ GetActualMaxPoint(XYZ tempMin, XYZ tempMax)
        {
            var maxActural = new XYZ
                (tempMin.X < tempMax.X ? tempMax.X : tempMin.X
                , tempMin.Y < tempMax.Y ? tempMax.Y : tempMin.Y
                , tempMin.Z < tempMax.Z ? tempMax.Z : tempMin.Z);
            return maxActural;
        }
    }
}
